/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package kuti.grp5.ui;

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.List;
import javax.swing.JSlider;
import kuti.EWorldElementType;
import kuti.IChargingStation;
import kuti.IKuti;
import kuti.IObstacle;
import kuti.ISimulParams;
import kuti.IWorldElement;
import kuti.grp5.Factory;
import kuti.ui.LogPanel;
import kuti.ui.UISimulationPanel;

/**
 * This class manages the clicks on the simulation panel when we are in the
 * manual map creation mode.
 * @author Jocelyn
 */
public class MapCreationMouseController extends MouseAdapter {

    private Point2D pointPressed;
    private double angle;
    private int doubleClickInterval = 100;
    private long lastClick = System.currentTimeMillis();
    private EWorldElementType customAddMode;
    private Factory factory;
    private UISimulationPanel sim;
    private ISimulParams isp;
    private LogPanel logs;
    private List<IWorldElement> elts;
    private JSlider sliderTau;

    public MapCreationMouseController(List<IWorldElement> elts, UISimulationPanel sim, ISimulParams isp, LogPanel logs, JSlider sliderTau) {
        this.sim = sim;
        this.isp = isp;
        this.elts = elts;
        this.logs = logs;
        this.sliderTau = sliderTau;
        factory = new Factory(isp);
    }

    /**
     * Event triggered when user clicks on the simulation panel.
     * Saves the point clicked in pointPressed.
     * @param evt MouseEvent triggered
     */
    @Override
    public void mousePressed(MouseEvent evt) {
        if (System.currentTimeMillis() - lastClick < doubleClickInterval) {
            // Double Click for future use...
        }

        pointPressed = evt.getPoint();
        pointPressed.setLocation(pointPressed.getX(), ISimulParams.WORLD_HEIGHT - pointPressed.getY());
        lastClick = System.currentTimeMillis();
    }

    /**
     * Event triggered when the mouse is dragged on the simulation panel.
     * Only used to repaint the angle of the Kuti when a Kuti is added manually.
     * @param evt MouseEvent triggered
     */
    @Override
    public void mouseDragged(MouseEvent evt) {
        if (pointPressed != null && customAddMode == EWorldElementType.KUTI && CollisionChecker.isKutiPlacementValid(pointPressed, elts, isp.getKDiameter())) {
            Point2D p = evt.getPoint();
            p.setLocation(p.getX(), ISimulParams.WORLD_HEIGHT - p.getY());

            angle = Math.atan2(p.getY() - pointPressed.getY(), p.getX() - pointPressed.getX());
            angle = angle < 0 ? angle + Math.PI * 2 : angle;

            IKuti k = factory.newKuti(pointPressed, angle, factory.newDriver());
            sim.drawKuti(k);
            sim.repaint();
        }
    }

    /**
     * Event triggered when the click is released on the simulation panel.
     * Used to print down a WorldElement when the mouse is released according
     * to the customAddMode. Placement is checked with the collision checker before
     * adding it.
     * @param evt MouseEvent triggered.
     */
    @Override
    public void mouseReleased(MouseEvent evt) {
        Point2D pointReleased = evt.getPoint();
        pointReleased.setLocation(pointReleased.getX(), ISimulParams.WORLD_HEIGHT - pointReleased.getY());
        if (customAddMode == EWorldElementType.KUTI) {
            if (CollisionChecker.isKutiPlacementValid(pointPressed, elts, isp.getKDiameter())) {
                IKuti k = factory.newKuti(pointPressed, angle, factory.newDriver());
                elts.add(k);
                logs.addEntry("Kuti added at " + pointReleased.toString() + " angle :" + (int) Math.toDegrees(angle) + "°");
                sim.drawKuti(k);
            } else {
                logs.addEntry("Non-valid position for Kuti placement.");
            }
        } else if (customAddMode == EWorldElementType.CHARGING_STATION) {
            Line2D diag = new Line2D.Double(pointPressed, pointReleased);
            Point2D center = new Point2D.Double(diag.getBounds().getCenterX(), diag.getBounds().getCenterY());
            int width = (int) Math.abs(pointPressed.getY() - pointReleased.getY());
            int height = (int) Math.abs(pointPressed.getX() - pointReleased.getX());
            if (CollisionChecker.isStationPlacementValid(center, pointPressed.distance(pointReleased) / 2, width, height, elts, isp.getKDiameter())) {
                IChargingStation ics = factory.newChargingStation(center, height, width, sliderTau.getValue());
                elts.add(ics);
                logs.addEntry("Charging station added at " + pointReleased.toString());
                sim.drawChargingStation(ics);
            } else {
                logs.addEntry("Non-valid position for Charging station placement.");
            }
        } else if (customAddMode == EWorldElementType.OBSTACLE
                || customAddMode == EWorldElementType.MAGNETIC_OBSTACLE) {
            Line2D line = new Line2D.Double(pointPressed, pointReleased);
            if (CollisionChecker.isObstaclePlacementValid(line, elts, isp.getKDiameter())) {
                addObstacle(customAddMode == EWorldElementType.MAGNETIC_OBSTACLE, line);
            } else {
                logs.addEntry("Non-valid position for Obstacle placement.");
            }
        }
        sim.repaint();
    }

    /**
     * Adds an obstacle (magnetic or not) according to the line given in parameters
     * @param magnetic - true if the obstacle is magnetic
     * @param line - Line2D representing the obstacle
     */
    private void addObstacle(boolean magnetic, Line2D line) {
        IObstacle obs = factory.newObstacle(line);
        if (magnetic) {
            obs = factory.newMagneticObstacle(obs);
        }
        elts.add(obs);
        if (magnetic) {
            logs.addEntry("Magnetic obstacle added");
        } else {
            logs.addEntry("Obstacle added");
        }
        sim.drawObstacle(obs, magnetic);
    }

    /**
     * Sets the customAddMode, indicating what the user wants to add on the
     * simulation panel.
     * @param customAddMode EWorldElementType that's going to be added when a
     * click occurs.
     */
    public void setCustomAddMode(EWorldElementType customAddMode) {
        this.customAddMode = customAddMode;
    }
}
